﻿using System;
using System.Drawing;
using System.Windows.Forms;
using CloudShot.Entities;
using CloudShot.Properties;
using CloudShot.Services;
using CloudShot.Utils.Drawings;

namespace CloudShot.States
{
	class ShotState : IShotState
	{
		private readonly Form _owner;
		private readonly string _storageName;
		private readonly Pen _borderPen = new Pen(Color.FromArgb(100, 100, 100), 1);

		private bool _pressed;

		private bool _selectionMoveEnabled;
		private bool _startXGreater;
		private bool _startYGreater;

		private bool _ctrlPressed;
		private Point _start;
		private Point _end;
		private Rectangle _selectionRect;

		private readonly OpenWindowsManager _windowManager = new OpenWindowsManager();
		private readonly Bitmap _lightBitmap;
		private string _typedText = string.Empty;
		private readonly Font _typedTextFontBig;
		private readonly Font _typedTextFontSmall;
		private readonly Font _storageFont;
		private readonly SolidBrush _fillBrush;

		public ShotState(Form owner, string storageName, Color fillColor)
		{
			_owner = owner;
			_storageName = storageName;

			_typedTextFontBig = new Font(_owner.Font.FontFamily, 15f);
			_typedTextFontSmall = new Font(_owner.Font.FontFamily, 11f, FontStyle.Regular);
			_storageFont = new Font(_owner.Font.FontFamily, 150f, FontStyle.Regular);
			_fillBrush = new SolidBrush(Color.FromArgb(130, fillColor));

			_lightBitmap = new Bitmap(_owner.BackgroundImage);
			Bitmap darkBitmap = new Bitmap(_lightBitmap);

			using (Graphics g = Graphics.FromImage(darkBitmap))
			{
				g.FillRectangle(_fillBrush, 0, 0, darkBitmap.Width, darkBitmap.Height);
			}

			_owner.BackgroundImage = darkBitmap;

			//subscribe
			_owner.KeyDown += OnKeyDown;
			_owner.KeyUp += OnKeyUp;
			_owner.KeyPress += OnKeyPress;
		}

		#region Event Handlers

		private void OnKeyDown(object sender, KeyEventArgs e)
		{
			if (_pressed && e.KeyCode == Keys.Space && !_selectionMoveEnabled)
			{
				_selectionMoveEnabled = true;
				_startXGreater = _end.X < _start.X;
				_startYGreater = _end.Y < _start.Y;
				return;
			}

			if (e.KeyCode == Keys.V && e.Modifiers == Keys.Control)
			{
				string text = Clipboard.GetText();
				if (!string.IsNullOrEmpty(text))
				{
					_typedText += text;
				}
			}
			else
			{
				_ctrlPressed = e.Control;
				_owner.Invalidate();
			}
		}

		private void OnKeyUp(object sender, KeyEventArgs e)
		{
			if (_selectionMoveEnabled)
			{
				_start.X = (_end.X == _selectionRect.Location.X) ? _end.X + _selectionRect.Width : _selectionRect.Location.X;
				_start.Y = (_end.Y == _selectionRect.Location.Y) ? _end.Y + _selectionRect.Height : _selectionRect.Location.Y;
				_selectionMoveEnabled = false;
			}

			_ctrlPressed = false;

			_owner.Invalidate();
		}

		private void OnKeyPress(object sender, KeyPressEventArgs e)
		{
			if (_pressed)
				return;

			if (e.KeyChar == '\b')//backspace
			{
				if (!string.IsNullOrEmpty(_typedText))
				{
					_typedText = _typedText.Remove(_typedText.Length - 1);
				}
			}
			else if (char.IsLetterOrDigit(e.KeyChar) || char.IsPunctuation(e.KeyChar) || char.IsWhiteSpace(e.KeyChar))
			{
				_typedText += e.KeyChar;
			}
		}

		#endregion

		#region Drawing

		public void Paint(Graphics graphics)
		{
			Point screenCursorPos = Cursor.Position;
			Screen currentScreen = Screen.FromPoint(screenCursorPos);
			Rectangle screenClientBounds = _owner.RectangleToClient(currentScreen.Bounds);

			_end = _owner.PointToClient(screenCursorPos);

			WindowInfo windowInfo = _windowManager.GetWindowRectFromPoint(screenCursorPos, new[] { _owner.Handle });

			if (windowInfo != null)
			{
				using (var pen = new Pen(Color.Green, 2))
				{
					//graphics.DrawRectangle(pen, _owner.RectangleToClient(wi.Bounds));
					Rectangle rect = _owner.RectangleToClient(windowInfo.Bounds);
					rect.Inflate(1, 1);
					graphics.DrawRoundedRectangle(pen, rect, 4);
				}
			}

			if (_pressed)
			{
				RecalculateSelection();
				Rectangle rect = _selectionRect;

				if (rect.Width <= 0 || rect.Height <= 0)
					return;

				graphics.DrawImage(_lightBitmap, rect.X, rect.Y, rect, GraphicsUnit.Pixel);
				if (!string.IsNullOrEmpty(_typedText))
					graphics.DrawPictureLabel(rect, _typedText, _typedTextFontSmall);
				graphics.DrawRectangle(_borderPen, rect);

				//Text label
				string label = string.Format("{0} x {1}", rect.Width, rect.Height);

				//show label on right bottom corner of selection rectangle
				graphics.DrawTextUnderCursor(label, new Point(rect.Right + 1, rect.Bottom + 1), _owner.Font, _owner.Width, _owner.Height, false);

				//show size label in center
				//SizeF labelSize = TextRenderer.MeasureText(label, _owner.Font);
				//if (rect.Width <= labelSize.Width || rect.Height <= labelSize.Height)
				//{
				//  DrawTextUnderCursor(label, new Point(rect.Right, rect.Bottom), graphics, false);
				//}
				//else
				//{
				//  graphics.FillRectangle(_fillBrush, rect.Left + (rect.Width / 2 - labelSize.Width / 2), rect.Top + (rect.Height / 2 - labelSize.Height / 2), labelSize.Width, labelSize.Height);
				//  TextRenderer.DrawText(graphics, label, _owner.Font, rect, Color.WhiteSmoke, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.LeftAndRightPadding);
				//}

				if (!_selectionMoveEnabled)
					graphics.DrawHint(Resources.TipHoldSpace, screenClientBounds, _selectionRect, _typedTextFontSmall);
			}
			else
			{
				string label = string.Format("{0} x {1}", screenCursorPos.X - currentScreen.Bounds.X, screenCursorPos.Y - currentScreen.Bounds.Y);

				graphics.DrawTextUnderCursor(label, _end, _owner.Font, _owner.Width, _owner.Height, true);

				if (_ctrlPressed)
					graphics.DrawCross(_owner.PointToClient(Cursor.Position), _owner.Width, _owner.Height);

				if (string.IsNullOrEmpty(_typedText) && string.IsNullOrEmpty(_storageName))
					return;

				if (!string.IsNullOrEmpty(_typedText))
					TextRenderer.DrawText(graphics, _typedText, _typedTextFontBig, screenClientBounds, Color.WhiteSmoke, TextFormatFlags.WordBreak | TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);

				if (!string.IsNullOrEmpty(_storageName))
				{
					SizeF size = graphics.MeasureString(_storageName, _storageFont, screenClientBounds.Width);
					graphics.DrawString(_storageName, _storageFont, new SolidBrush(Color.FromArgb(25, Color.WhiteSmoke)), (screenClientBounds.Width - size.Width) / 2, (screenClientBounds.Height - size.Height) / 2);
				}
			}
		}

		#endregion

		#region IShotState

		public void Start(Point location)
		{
			_start = location;
			_pressed = true;
		}

		public virtual void Update(Point location)
		{
		}

		public CaptureInfo End()
		{
			_pressed = false;

			Rectangle shotRect = _start == _end ? GetWindowRectangle() : _selectionRect;

			Unsubscribe();
			DisposeGdiObjects();

			return new CaptureInfo
			{
				ClientBounds = shotRect,
				ScreenBounds = _owner.RectangleToScreen(shotRect),
				LabelText = _typedText
			};
		}

		private Rectangle GetWindowRectangle()
		{
			Rectangle shotRect = Rectangle.Empty;
			Point screenPoint = _owner.PointToScreen(_start);
			WindowInfo window = _windowManager.GetWindowRectFromPoint(screenPoint, new[] { _owner.Handle });

			if (window != null)
			{
				//if window is greater then screen, we must crop window by screen size
				//window.Bounds.Intersect( Owner.DesktopBounds );
				shotRect = _owner.RectangleToClient(window.Bounds);
				shotRect.Intersect(_owner.RectangleToClient(_owner.Bounds));
			}

			return shotRect;
		}

		#endregion

		private void Unsubscribe()
		{
			_owner.KeyDown -= OnKeyDown;
			_owner.KeyUp -= OnKeyUp;
			_owner.KeyPress -= OnKeyPress;
		}

		private void DisposeGdiObjects()
		{
			_typedTextFontBig.Dispose();
			_typedTextFontSmall.Dispose();
			_storageFont.Dispose();
			_borderPen.Dispose();
			_fillBrush.Dispose();
			_lightBitmap.Dispose();
		}

		private void RecalculateSelection()
		{
			if (_selectionMoveEnabled)
			{
				_selectionRect.X = _startXGreater ? _end.X : _end.X - _selectionRect.Width;
				_selectionRect.Y = _startYGreater ? _end.Y : _end.Y - _selectionRect.Height;

				if (_selectionRect.X < 0)
					_selectionRect.X = 0;

				if (_selectionRect.Y < 0)
					_selectionRect.Y = 0;

				if (_selectionRect.X + _selectionRect.Width > _lightBitmap.Width)
					_selectionRect.X = _lightBitmap.Width - _selectionRect.Width;

				if (_selectionRect.Y + _selectionRect.Height > _lightBitmap.Height)
					_selectionRect.Y = _lightBitmap.Height - _selectionRect.Height;
			}
			else
			{
				_selectionRect.X = Math.Min(_start.X, _end.X);
				_selectionRect.Width = Math.Abs(_start.X - _end.X);

				_selectionRect.Y = Math.Min(_start.Y, _end.Y);
				_selectionRect.Height = Math.Abs(_start.Y - _end.Y);
			}
		}
	}
}